home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / PCI Driver Sample / NCR_DriverProject / Src / NCRRunScript.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-07  |  37.1 KB  |  1,045 lines  |  [TEXT/MPCC]

  1. /*                                        NCRRunScript.c                                */
  2. /*
  3.  * NCRRunScript.c
  4.  * Copyright © 1994 Apple Computer Inc. All rights reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | These low-level routines access the NCR 53C825 chip registers. They start an        |
  8.       | asynchrous operation (either a general SCSI I/O request or a Bus Reset operation)    |
  9.       | and follow its operation to the bitter end. While the logic is extremely specific    |
  10.       | to the NCR chip, you should at least read through this file to understand the        |
  11.       | relation between the hardware-specific code and the Driver Services code.            |
  12.     |                                                                                    |
  13.     | Almost everything here is specific to the NCR chip. The watchdog timeout will        |
  14.     | be generally useful. However, it has not been tested yet.                            |
  15.     .___________________________________________________________________________________.
  16. */
  17.  
  18. #include "NCRDriverPrivate.h"
  19. /*
  20.  * stddef defines offsetof()
  21.  */
  22. #include <stddef.h>
  23. /*
  24.  * WARNING: the bus reset script hangs if you turn on single-stepping. I suspect that
  25.  * I'm not handling multiple interrupt conditions correctly. I "fixed" this by using
  26.  * the DriverServices DelayForHardware routine to do bus reset delays. However, in
  27.  * general, you should not expect single-step to work correctly.
  28.  */
  29. #define SINGLE_STEP                0    /* Used for debugging the script */
  30. #ifndef SINGLE_STEP_DEFAULT
  31. #define SINGLE_STEP_DEFAULT        0
  32. #endif
  33.  
  34. void                        StartWatchdogTimeout(
  35.         register PerRequestDataPtr perRequestDataPtr
  36.     );
  37. OSStatus                    WatchdogTimerCompletion(
  38.         void                    *p1,
  39.         void                    *p2
  40.     );
  41. void                        StartScript(
  42.         register PerRequestDataPtr perRequestDataPtr,
  43.         UInt32                    scriptPtr
  44.     );
  45.  
  46. #define REQUEST        (*perRequestDataPtr)
  47. #define SCRIPT        (REQUEST.scriptData)
  48. #define SHADOW        (REQUEST.shadow)
  49.  
  50. void                        StoreDMAParameters(
  51.         PerRequestDataPtr        perRequestDataPtr
  52.     );
  53.  
  54. #if USE_LOG_LIBRARY
  55. void                        LogScriptInterrupt(
  56.         register PerRequestDataPtr perRequestDataPtr
  57.     );
  58. void                        DumpRegisters(
  59.         register PerRequestDataPtr perRequestDataPtr
  60.     );
  61. #else
  62. #define LogScriptInterrupt(per)                /* Nothing */
  63. #define DumpRegisters(perRequestDataPtr)    /* Nothing */
  64. #endif
  65.  
  66.  
  67. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  68.  * This is the primary interrupt service routine that is called by the system. Input
  69.  * parameters are ignored.
  70.  *
  71.  * This subroutine handles the interrupt from the NCR chip. The logic is from the
  72.  * NCR Family Programmers Guide, page 8-30 ff.
  73.  *
  74.  * Note that we must handle phase-mismatch errors here. In particular, we must handle
  75.  * residual byte count problems (Request Sense with a larger buffer than the
  76.  * device plans to deliver) as they are not fatal errors.
  77.  */
  78. InterruptMemberNumber
  79. PCIInterruptServiceRoutine(
  80.         InterruptSetMember        member,
  81.         void                    *refCon,
  82.         UInt32                    theIntCount
  83.     )
  84. {
  85.         OSErr                    status;
  86.         register PerRequestDataPtr perRequestDataPtr;
  87.         unsigned short            oldPhase;
  88.         InterruptMemberNumber    result;
  89.         IOParam                    *pb;
  90.         NCRSCSIParamPtr            scsiParamPtr;
  91.         short                    stallLoop;
  92.         static const Nanoseconds gTenMsec = { 0, 10000000 };
  93. #define PB        (*pb)
  94. #define SCSI    (*scsiParamPtr)
  95.  
  96.         Trace(PCIInterruptServiceRoutine);
  97.         UNUSED(member);
  98.         UNUSED(refCon);
  99.         UNUSED(theIntCount);
  100.         /*
  101.          * We should try to establish which "per-request" interrupted the processor.
  102.          * Currently, we support only a single transfer, but this routine will have to
  103.          * get more complex (by querying the NCR chip) when we support suspend-resume
  104.          * or tagged commands. For example, we could use the data storage address.
  105.          * The actual interrupt is processed by a primary interrupt routine. When the
  106.          * I/O operation is completed, it queues a secondary interrupt routine that
  107.          * frees resources and calls IOCommandIsComplete. 
  108.          */
  109.         perRequestDataPtr = GLOBAL.perRequestDataPtr;
  110.         /*
  111.          * These are only needed for re-preparation.
  112.          */
  113.         pb = (IOParam *) REQUEST.pb;                    /* pb can be NULL            */
  114.         scsiParamPtr = (pb == NULL) ? NULL : (NCRSCSIParamPtr) PB.ioMisc;
  115.         status = kIOBusyStatus;                        /* Changed by completed I/O        */
  116.         SHADOW.istat = ReadByte(ISTAT);                /* 0x14 Interrupt Status        */
  117.         /*
  118.          * We must check that at least one interrupt bit is set -- if none are set,
  119.          * this is a spurious interrupt -- we then return kIsrIsNotComplete
  120.          */
  121.         result = ((SHADOW.istat & (bit2 | bit1 | bit0)) != 0)
  122.                     ? kIsrIsComplete : kIsrIsNotComplete;
  123.         if ((SHADOW.istat & bit2) != 0) {            /* INTF: Interrupt on the fly?    */
  124.             /*
  125.              * This bit must be written to 1 to clear it. This interrupt must be
  126.              * handled before handling other interrupt conditions. We don't
  127.              * currently use the INTF interrupt.
  128.              */
  129.             WriteByte(ISTAT, SHADOW.istat | bit2);
  130.             SynchronizeIO();                            /* eieio                    */
  131.             SHADOW.istat = ReadByte(ISTAT);                /* 0x14 Int. Status            */
  132.         }
  133.         if ((SHADOW.istat & (bit1 | bit0)) != 0) {
  134.             /*
  135.              * If the SIP and DIP interrupts are clear, this is a "true" interrupt on
  136.              * the fly. Exit the interrupt service routine to let the script continue
  137.              * in peace -- we're not allowed to touch other NCR chip registers.
  138.              * If either bit is set, this is a normal interrupt. Save the interesting
  139.              * registers in the per-request block: this is for debugging convenience.
  140.              *
  141.              * Note: we might need to fix this algorithm. See the discussion in the
  142.              * NCR 53C825 manual for the reason behind clearing ISTAT.
  143.              */
  144. #if 0
  145.             if ((SHADOW.istat & (bit1 | bit0)) == bit0)
  146.                 WriteByte(ISTAT, 0);
  147. #endif
  148.             SHADOW.dbc = ReadLong(DBC);                    /* 0x24 byte count            */
  149.             SHADOW.dsp = ReadLong(DSP);                    /* 0x2C Script pointer        */
  150.             SHADOW.scriptPCOffset = SHADOW.dsp - REQUEST.scriptBaseAddress - 8;
  151. #if 1 && USE_LOG_LIBRARY
  152.             WriteLogEntry(GLOBAL.logRecordPtr, 'PISR',
  153.                 LogFormat4(kLogFormatAddress, kLogFormatAddress, kLogFormatSigned, kLogFormatString),
  154.                 (UInt32) SHADOW.dsp,
  155.                 (UInt32) REQUEST.scriptBaseAddress,
  156.                 (SInt32) SHADOW.scriptPCOffset,
  157.                 "\pScript @ isr"
  158.             );
  159. #endif
  160.             SHADOW.dsps = ReadLong(DSPS);                /* 0x30 Script 2nd long    */
  161.             /*
  162.              * Read sist0 and sist1 before reading dstat. The calls to
  163.              * DelayForHardware enforce a 12 clock tick delay between reads.
  164.              */
  165.             SHADOW.sist0 = ReadByte(SIST0);                /* 0x42 SCSI Int. Sts 0        */
  166.             DelayForHardware(GLOBAL.clockTick12);
  167.             SHADOW.sist1 = ReadByte(SIST1);                /* 0x43 SCSI Int. Sts 1        */
  168.             DelayForHardware(GLOBAL.clockTick12);
  169.             SHADOW.dstat = ReadByte(DSTAT);                /* 0x0C DMA status            */
  170.             SHADOW.scntl1 = ReadByte(SCNTL1);            /* 0x01 SCSI control 1        */
  171.             SHADOW.socl = ReadByte(SOCL);                /* 0x09 SCSI output ctl        */
  172.             SHADOW.sbcl = ReadByte(SBCL);                /* 0x0B Bus ctl signals        */
  173.             SHADOW.sstat0 = ReadByte(SSTAT0);            /* 0x0D core status            */
  174.             SHADOW.sstat1 = ReadByte(SSTAT1);            /* 0x0E core status            */
  175.             SHADOW.sstat2 = ReadByte(SSTAT2);            /* 0x0F core status            */
  176.             SHADOW.dfifo = ReadByte(DFIFO);                /* 0x20    fifo count            */
  177.             SHADOW.dbc &= 0x00FFFFFF;                    /* Only 24 bits                */
  178.             LogScriptInterrupt(perRequestDataPtr);        /* Debug: log registers        */
  179.             if ((SHADOW.istat & bit0) != 0) {            /* DMA interrupt pending?    */
  180.                 if ((SHADOW.dstat & bit7) == 0) {        /* DMA fifo not empty?        */
  181.                     LogHex(SHADOW.dstat, "\pDMA fifo not empty");
  182.                     /*
  183.                      * Clear the dma fifo non-empty condition by resetting the dma
  184.                      * and scsi fifos. Hmm, how does this deal with partial (short)
  185.                      * preparation?
  186.                      */
  187.                     SHADOW.stest3 = ReadByte(STEST3);
  188.                     WriteByte(STEST3, SHADOW.stest3 | bit1);
  189.                     SynchronizeIO();                    /* eieio                    */
  190.                     SHADOW.ctest3 = ReadByte(CTEST3);
  191.                     WriteByte(CTEST3, SHADOW.ctest3 | bit2);
  192.                 }
  193.                 if ((SHADOW.dstat & (bit5 | bit6)) != 0) {    /* PCI bus fault?        */
  194.                     LogHex(SHADOW.dstat, "\pPCIBusFault");
  195.                     LogHex(ReadLong(DBC), "\pDBC 0x24-26, DNAD 0x27 - count, cmd");
  196.                     LogHex(ReadLong(DNAD), "\pDNAD 0x28 - DMA next address");
  197.                     LogHex(ReadLong(DSPS), "\pDSPS 0x30 - Scripts pointer save");
  198.                     status = scsiTerminated;
  199.                 }
  200.                 else if ((SHADOW.dstat & bit0) != 0) {    /* Illegal Script opcode?    */
  201.                     LogHex(SHADOW.scriptPCOffset, "\pIllegal Script @ pc offset");
  202.                     DumpRegisters(perRequestDataPtr);
  203.                     status = ioErr;
  204.                 }
  205.                 else if ((SHADOW.dstat & bit4) != 0) {    /* Abort error (IOKill)        */
  206.                     /*
  207.                      * Note: we'll get one of these if the non-interrupt variant
  208.                      * times out. A better error message mechanism is probably needed.
  209.                      * A KillIO request also causes one of these.
  210.                      */
  211.                     LogString("\pChip-detected abort");
  212.                     WriteByte(ISTAT, 0);                /* Clear ISTAT again        */
  213.                     /*
  214.                      * To do: look at the current bus status: if we are connected
  215.                      * and REQ is set, the target is trying to do something and we
  216.                      * aren't responding. We should restart the script at the
  217.                      * rundown address. If we are connected and the target is
  218.                      * not in REQ, the device is, how should we put it, dead. About
  219.                      * all we can do is exit the script and hope that the caller
  220.                      * tosses a Bus Reset or I/O Rundown Control call at us.
  221.                      */
  222.                     status = scsiCommandTimeout;
  223.                 }
  224.                 else if ((SHADOW.dstat & bit2) != 0) {    /* Script INT instruction        */
  225.                     //** LogDecimal(SHADOW.dsps, "\pScript Interrupt");
  226.                     WriteByte(STIME0, 0);                /* Cancel timers            */
  227.                     WriteByte(STIME1, 0);                /* Cancel both of them        */
  228.                     REQUEST.dmaFirstPrepared += REQUEST.dmaLengthPrepared;
  229.                     status = SHADOW.dsps;
  230.                     if (status == kIntNeedAnotherPreparation) {
  231.                         if (scsiParamPtr != NULL
  232.                           && SCSI.driverAction != kNCRDriverNoDataPhase) {
  233.                             /*
  234.                              * We are in a data phase, but don't have any prepared I/O.
  235.                              * Prepare the next chunk of DMA and restart the script.
  236.                              * If PrepareNextDMA returns scsiDataRunError, queue the
  237.                              * Secondary Interrupt Handler to schedule our Software
  238.                              * Task that will call PrepareMemoryForIO, setup the next
  239.                              * chunk of the transfer, and then queue the Secondary
  240.                              * Interrupt routine again to restart the device.
  241.                              */
  242. #if 0
  243.                             LogDecimal(
  244.                                 (SInt32) ReadByte(CTEST0),
  245.                                 "\pCalling Prepare[Next Area]"
  246.                             );
  247. #endif
  248.                             status = PrepareNextDMA(perRequestDataPtr);
  249.                             if (status == scsiDataRunError)
  250.                                 status = kPrepareMemoryStartTask;
  251.                             else {
  252.                                 StoreDMAParameters(perRequestDataPtr);
  253.                                 WriteByte(CTEST0, SCSI.driverAction);
  254.                                 status = kIOBusyStatus;
  255.                             }
  256.                         }
  257.                         else {
  258.                             status = scsiTransferTypeInvalid;    /* Oops            */
  259.                         }
  260.                     }
  261.                     else {
  262.                         /*
  263.                          * This is some other "final" SCSI interrupt status.
  264.                          */
  265.                     }
  266.                 }
  267.                 if ((SHADOW.dstat & bit3) != 0) {
  268.                     /*
  269.                      * Single-step interrupt. We have presumably logged the interrupt
  270.                      * so we need only restart the process. This may be insufficient:
  271.                      * we might need to re-read the registers to see if other interrupt
  272.                      * conditions need to be cleared.
  273.                      *
  274.                      * Warning: the single-step stuff doesn't work correctly as the
  275.                      * chip apparently gives us multiple interrupt conditions which
  276.                      * the interrupt service routine doesn't handle properly.
  277.                      */
  278.                 }
  279.             } /* If DMA interrupt or script complete */
  280.             if ((SHADOW.istat & bit1) != 0) {        /* SIP: SCSI interrupt pending?    */
  281.                 /*
  282.                  * Process a SCSI interrupt.
  283.                  */
  284.                 if ((SHADOW.sist1 & bit2) != 0) {        /* Selection timeout?        */
  285.                     WriteByte(STIME0, 0);                /* Clear the timer            */
  286.                     status = scsiSelectTimeout;
  287.                 }
  288.                 else if ((SHADOW.sist1 & bit1) != 0) {    /* General timeout?            */
  289.                     WriteByte(STIME1, 0);                /* Clear the timer            */
  290.                 }
  291.                 else if ((SHADOW.sist0 & (bit3 | bit2 | bit1 | bit0)) == bit1) {
  292.                     /*
  293.                      * Bus Reset. If SCSI Control 1 has RST (bit 3) set, indicating
  294.                      * that we asserted bus Reset, generate a private status that
  295.                      * the secondary interrupt routine will use to stall the script
  296.                      * for 250 msec. Then, it restarts the script to clear the
  297.                      * bus reset condition.
  298.                      */
  299.                     if ((SHADOW.scntl1 & bit3) != 0) {
  300.                         status = kBusResetRestart;        /* Private status            */
  301.                     }
  302.                     else {
  303.                         status = scsiSCSIBusReset;
  304.                         LogString("\pUnexpected bus reset");
  305.                     }
  306.                 }
  307.                 else if ((SHADOW.sist0 & (bit3 | bit2 | bit1 | bit0)) != 0) {
  308.                     /*
  309.                      * bit3: SCSI gross error (data under/overflow) 
  310.                      * bit2: Unexpected disconnect
  311.                      * bit1: Bus Reset (and some other SCSI interrupt)
  312.                      * bit0: Bus parity error.
  313.                      */
  314.                     if (REQUEST.scriptSelector == kSCSICommandScript) {
  315.                         switch (SHADOW.sist0 & (bit3 | bit2 | bit1 | bit0)) {
  316.                         case bit0:    status = scsiParityError;        break;
  317.                         case bit1:    status = scsiSCSIBusReset;        break;
  318.                         case bit2:    status = scsiUnexpectedBusFree;    break;
  319.                         case bit3:    status = scsiDataRunError;        break;
  320.                         default:    status = scsiTerminated;        break;
  321.                         }
  322.                     }
  323.                     if (status == scsiSCSIBusReset                    /* Bus reset?    */
  324.                      && REQUEST.scriptSelector == kBusResetScript)    /* Ignore ours    */
  325.                         status = noErr;
  326.                     else {
  327.                         LogHex(SHADOW.sist0, "\pStrange SCSI error");
  328.                     }
  329.                 }
  330.                 else if ((SHADOW.sist0 & bit7) != 0) {        /* Phase mismatch?        */
  331.                     /*
  332.                      * Phase mismatch - this is often an error, but we do allow
  333.                      * short transfers, where the user requested a longer buffer
  334.                      * than the target intends to provide. We check this by
  335.                      *    (1) checking for "old" phase in SHADOW.socl. Look for DATI.
  336.                      *    (2) checking for "new" phase -- it should be STATUS, but this
  337.                      *        is only for debugging: the script will eventually fail if
  338.                      *        this is not the case.
  339.                      *    (3) checking the DMA fifo -- it should be empty
  340.                      *    (4) checking the DBC transfer count -- it should be nonzero.
  341.                      * Other cases are errors. The script continues after phase-
  342.                      * mismatch errors: it will eventually finish with a bus-free
  343.                      * status and an Int instruction.
  344.                      *
  345.                      * To handle partial preparationn, scatter-gather, or other
  346.                      * non-contiguous physical memory situations, we queue a secondary
  347.                      * interrupt handler that recovers the residual dma count (the
  348.                      * following code will be copied there), it then calls
  349.                      * PrepareMemoryForIO to update the physical mapping pointers,
  350.                      * updates the data table, and restarts the operation. Too much
  351.                      * work for a sample.
  352.                      */
  353.                     oldPhase = SHADOW.socl & 0x07;
  354.                     if (oldPhase == DATO || oldPhase == DATI) {
  355.                         /*
  356.                          * Try to recover the residual dma count. The algorithm is
  357.                          * from page 5-26 of the  NCR data manual:
  358.                          *    1. Subtract the seven least significant bits of the dbc
  359.                          *        register from the 7-bit value of the dfifo register.
  360.                          *    2. And the result with 0x7F for a byte count between
  361.                          *        zero and 64.
  362.                          */
  363.                         SHADOW.residualTransferCount = SHADOW.dbc
  364.                             + (((SHADOW.dfifo & 0x7F) - (SHADOW.dbc & 0x7F)) & 0x7F);
  365.                         /*
  366.                          * Byte count will be a value between zero and 64.
  367.                          * If this was a send (DATO, MSGO, CMD) operation, look
  368.                          * at the SSTAT0 and SSTAT2 registers to see if bytes remain
  369.                          * in the SODL register. This probably needs work.
  370.                          */
  371.                         if (oldPhase == DATO) {
  372.                             if ((SHADOW.sstat0 & bit5) != 0)
  373.                                 ++SHADOW.residualTransferCount;
  374.                             if ((SHADOW.sstat2 & bit5) != 0)
  375.                                 ++SHADOW.residualTransferCount;
  376.                         }
  377.                         else {
  378.                             if ((SHADOW.sstat0 & bit7) != 0)
  379.                                 ++SHADOW.residualTransferCount;
  380.                             if ((SHADOW.sstat2 & bit7) != 0)
  381.                                 ++SHADOW.residualTransferCount;
  382.                         }
  383.                         if (pb != NULL)
  384.                             PB.ioActCount -= SHADOW.residualTransferCount;
  385.                     } /* If DATI or DATO phase */
  386.                 } /* If this is a phase mismatch error */
  387.             }
  388.         }
  389.         if (status != kIOBusyStatus) {
  390.             /*
  391.              * Turn off the interrupts and chip timers to prevent an unexpected
  392.              * interrupt: our global per-request record risks reentrancy bugs.
  393.              * If we restart I/O we will re-enable timers and interrupts.
  394.              */
  395.             WriteByte(DIEN, 0);
  396.             WriteByte(SIEN0, 0);
  397.             WriteByte(SIEN1, 0);
  398.             WriteByte(STIME0, 0);
  399.             WriteByte(STIME1, 0);
  400.             /*
  401.              * Note that actual completion is done from a secondary interrupt
  402.              * handler as we must Checkpoint the I/O buffers.
  403.              */
  404.             for (stallLoop = 0; stallLoop < 100; stallLoop++) {
  405.                 status = NCRQueueSecondaryInterrupt(perRequestDataPtr, status);
  406.                 if (status == noErr)
  407.                     break;
  408.                 /*
  409.                  * For some reason, we couldn't queue a secondary interrupt handler
  410.                  * Try after a brief stall. This probably won't work and certainly
  411.                  * won't be tested adaquately.
  412.                  */
  413.                 LogStatusString(status, "\pQueueSecondaryInterruptHandler fail");
  414.                 DelayForHardware(gTenMsec);
  415.             }
  416.         }
  417.         else {
  418.             StartScript(perRequestDataPtr, SHADOW.dsp);
  419.         }
  420.         /*
  421.          * Jump directly to exit to leave the interrupt service routine without
  422.          * restarting the script or scheduling a secondary interrupt routine.
  423.          */
  424. exit:    return (result);
  425. #undef PB
  426. #undef SCSI
  427. }
  428.  
  429. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  430.  * NCRQueueSecondaryInterrupt
  431.  *
  432.  * This routine queues the secondary interrupt handler. It was centralized to help
  433.  * debug the system ROMs.
  434.  */
  435. OSErr
  436. NCRQueueSecondaryInterrupt(
  437.         PerRequestDataPtr        perRequestDataPtr,
  438.         OSErr                    statusOrSignal
  439.     )
  440. {
  441.         OSStatus                osStatus;
  442.         
  443.         Trace(NCRQueueSecondaryInterrupt);
  444. LogStatusString(statusOrSignal, "\pAt NCRQueueSecondaryInterrupt");
  445.         osStatus = QueueSecondaryInterruptHandler(
  446.                     NCRSecondaryInterruptHandler,
  447.                     NULL,
  448.                     (void *) perRequestDataPtr,
  449.                     (void *) statusOrSignal
  450.                 );
  451.         CheckStatus(osStatus, "\pQueueSecondaryInterruptHandler2");
  452. LogStatusString(statusOrSignal, "\pQueueSecondaryInterruptHandler returns");
  453.         return (osStatus);
  454. }
  455.  
  456. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  457.  * NCRSecondaryInterruptHandler
  458.  *
  459.  * This is a secondary interrupt handler that is called by the primary interrupt
  460.  * service routine to complete an asynchronous I/O request. It is also called
  461.  * directly by the KillIO handler. It uses the atomic test-and-set operation to
  462.  * ensure that each request is completed exactly once.
  463.  *
  464.  * This is also called to restart I/O from the Software Interrupt Task
  465.  */
  466. OSStatus
  467. NCRSecondaryInterruptHandler(
  468.         void                    *p1,
  469.         void                    *p2
  470.     )
  471. {
  472.         register PerRequestDataPtr    perRequestDataPtr;
  473.         OSErr                    ioResult;
  474.         /*
  475.          * Note that status is used here only for adminstrative status from the
  476.          * Driver Services Library calls -- it is never passed back to the caller.
  477.          */
  478.         OSErr                    status;
  479.         OSStatus                osStatus;
  480.         ParmBlkPtr                pb;
  481.         NCRSCSIParamPtr            scsiParamPtr;
  482.         UInt32                    scriptPtr;
  483. #define IOPB    (pb->ioParam)
  484. #define SCSI    (*scsiParamPtr)
  485.  
  486. LogStatusString((UInt32) p2, "\pIn Secondary Handler");
  487.         Trace(NCRSecondaryInterruptHandler);
  488.         perRequestDataPtr = (PerRequestDataPtr) p1;
  489.         pb = REQUEST.pb;
  490.         ioResult = (OSErr) p2;
  491.         /*
  492.          * There are a couple of private ioResult values that are used for
  493.          * intermediate script operations.
  494.          */
  495.         switch (ioResult) {
  496.         case kIORequestStart:
  497.             SHADOW.residualTransferCount = 0;
  498.             switch (REQUEST.scriptSelector) {
  499.             case kSCSICommandScript:
  500. LogString("\pStart SCSI I/O");
  501.                 WriteByte(SCID, GLOBAL.initiatorID);
  502.                 StoreDMAParameters(perRequestDataPtr);
  503.                 /*
  504.                  * Timeout if selection fails after 409.6 msec. This is the smallest
  505.                  * value larger than the SCSI Standard 250 msec. timeout value.
  506.                  */
  507.                 WriteByte(STIME0, 13);                            /* Selection timer    */
  508.                 WriteLong(DSA, REQUEST.scriptDataPtr);
  509.                 SynchronizeIO();                                /* eieio            */
  510.                 break;
  511.             case kBusResetScript:
  512. LogString("\pStart Bus Reset");
  513.                 /*
  514.                  * No initiator ID, selection timeout or DSA.
  515.                  */
  516.                 WriteByte(SCID, GLOBAL.initiatorID);
  517.                 WriteByte(STIME0, 0);
  518.                 WriteLong(DSA, kInvalidPageAddress);
  519.                 break;
  520.             case kSCSITestISRScript:
  521.             case kSCSITestMemoryScript:
  522.                 StoreDMAParameters(perRequestDataPtr);
  523.  
  524. LogMemory(&REQUEST.memoryMoveScript[0], sizeof REQUEST.memoryMoveScript);
  525. LogMemory((void *) REQUEST.scriptPtr, sizeof REQUEST.memoryMoveScript);
  526.  
  527.                 WriteLong(DSA, kInvalidPageAddress);
  528.                 break;
  529.             }
  530.             StartWatchdogTimeout(perRequestDataPtr);
  531.             StartScript(perRequestDataPtr, REQUEST.scriptPtr);
  532.             break;
  533.         case kPrepareMemoryStartTask:    /* Primary Interrupt needs PrepareMemory    */
  534.             /*
  535.              * We've run off the end of the preparation. Queue a Software Interrupt
  536.              * that will call PrepareMemoryForIO to handle the partial preparation.
  537.              * If this fails, restart I/O to rundown the SCSI device. Because this
  538.              * will run "task level" and other I/O may intervene, we may want to
  539.              * cancel the timer (for now) and restart it when the driver resumes
  540.              * operation. This requires thought, but I don't know the right answer;
  541.              * but I'll try it for now.
  542.              *
  543.              * Hmm, to do this right, CancelWatchdogTimer ought to return "time
  544.              * remaining" which will be used to restart the timer, rather than
  545.              * always starting at the original timeout.
  546.              */
  547. needAnotherPrepareMemory:
  548.             CancelWatchdogTimer(perRequestDataPtr);
  549.             osStatus = SendSoftwareInterrupt(REQUEST.nextDMAInterruptID, 0);
  550.             CheckStatus(osStatus, "\pSendSoftwareInterrupt");
  551.             if (osStatus != noErr) {
  552.                 /*
  553.                  * We couldn't queue a software interrupt. Force the
  554.                  * device into its failure script. The device will, eventually,
  555.                  * exit and return an error ioResult to the caller.
  556.                  */
  557.                 WriteByte(CTEST0, 0);    /* This will cause rundown    */
  558.                 scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  559.                             + kSCSIRundownScript;
  560.                 StartScript(perRequestDataPtr, scriptPtr);
  561.             }
  562.             break;        
  563.         case kPrepareMemoryRestart:        /* PrepareMemoryForIO called                */
  564. LogString("\pPrepMemory restart");
  565.             StoreDMAParameters(perRequestDataPtr);
  566.             StartWatchdogTimeout(perRequestDataPtr);
  567.             if (REQUEST.scriptSelector == kSCSITestMemoryScript) {
  568.                 scriptPtr = ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  569.                         + offsetof(PerRequestData, memoryMoveScript);
  570.             }
  571.             else {
  572.                 scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  573.                         + kSCSIRestartScript;
  574.             }
  575.             StartScript(perRequestDataPtr, scriptPtr);
  576.             break;
  577.         case kBusResetRestart:            /* Bus reset: stall and restart the script    */
  578. LogString("\pBus Reset restart");
  579.             DelayForHardware(GLOBAL.msec250);
  580.             scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  581.                         + kBusResetScriptRestart;
  582.             StartScript(perRequestDataPtr, scriptPtr);
  583.             break;
  584.         /*
  585.          * These are intermediate error values: restart at 'Fail'
  586.          */
  587.         case kIntFailStrangePhase:        /* Bug: unknown phase at phase loop        */
  588.         case kIntDataPhaseExpected:        /* At 'Data', but not in data phase        */
  589.         case kIntPreparationFailed:        /* CTEST == 0 after prep restart        */
  590.         case kIntDataOutNoData:            /* CTEST not == 2 at DATO phase            */
  591.         case kIntDataInNoData:            /* CTEST not == 1 at DATI phase            */
  592.         case kIntNotMsgInAfterStatus:    /* STS phase must be followed by MSGI    */
  593.             LogStatusString(ioResult, "\pIntermediate Script Error");
  594.             scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  595.                             + kSCSIRundownScript;
  596.             StartScript(perRequestDataPtr, scriptPtr);
  597.             break;
  598.         case noErr:                        /* Successful completion                */
  599.             /*
  600.              * But wait, there's more -- the memory test will succeed after each
  601.              * "chunk" of DMA -- we catch this here and re-direct (to use a legal
  602.              * term) the secondary interrupt to the Software Task.
  603.              */
  604.             if (REQUEST.scriptSelector == kSCSITestMemoryScript
  605.              && IOPB.ioActCount < IOPB.ioReqCount)
  606.                  goto needAnotherPrepareMemory;
  607.              /* Else, continue at I/O completion */
  608.         default:
  609.             /*
  610.              * Note that we use an atomic memory sequence to retrieve the old value of
  611.              * the parameter block, then set it to NULL. This prevents two asynchronous
  612.              * routines from calling IOCommandIsComplete on the same parameter block.
  613.              * Ignoring atomic considerations, the while/if sequence is equivalent to
  614.              *        pb = REQUEST.pb;
  615.              *        if (pb != NULL) {
  616.              *            REQUEST.pb = NULL;
  617.              *            ... IOCommandIsComplete(...);
  618.              *        }
  619.              */
  620. #if 1 && USE_LOG_LIBRARY
  621.             WriteLogEntry(GLOBAL.logRecordPtr, 'SecC',
  622.                 LogFormat4(
  623.                     kLogFormatSigned, kLogFormatAddress,
  624.                     kLogFormatAddress, kLogFormatString
  625.                 ),
  626.                 (signed long) ioResult, REQUEST.pb,
  627.                 (((unsigned long) SCRIPT.commandCompleteByte) << 16) | SCRIPT.statusByte,
  628.                 "\pI/O complete"
  629.             );
  630. #endif
  631.             /*
  632.              * Watch out for re-entrancy problems here. Once we call IOCommandIsComplete,
  633.              * we must not continue the while loop as the REQUEST and REQUEST.pb may be
  634.              * re-used because of an asynchronous request started from the completion
  635.              * routine.
  636.              *
  637.              * Since we're done with this I/O request, checkpoint the per-request
  638.              * table, but don't release system resources.
  639.              *
  640.              * Note: status values are not passed back to the caller -- ioResult has
  641.              * the final operation status.
  642.              */
  643.             status = CheckpointIO(
  644.                         REQUEST.perRequestIOTable.preparationID,
  645.                         kMoreIOTransfers
  646.                     );
  647.             CheckStatus(status, "\pCheckpointIO perRequest table");
  648. LogString("\pAfter checkpoint perRequest table");
  649.             while ((pb = REQUEST.pb) != NULL) {
  650.                 if (IOPB.ioResult != kIOBusyStatus) {
  651.                     /*
  652.                      * We can be re-entered after KillIO. This needs improvement.
  653.                      */
  654.                     LogStatusString(IOPB.ioResult, "\pCompletion re-entered!!!");
  655.                     break;
  656.                 }
  657.                 if (CompareAndSwap((UInt32) pb, NULL, (UInt32 *) &REQUEST.pb)) {
  658.                     CancelWatchdogTimer(perRequestDataPtr);
  659. LogString("\pAfter I/O complete cancel watchdog");
  660.                     if (REQUEST.scriptSelector == kSCSICommandScript) {
  661.                         scsiParamPtr = (NCRSCSIParamPtr) IOPB.ioMisc;
  662.                         SCSI.statusByte = SCRIPT.statusByte;
  663.                         SCSI.messageByte = SCRIPT.commandCompleteByte;
  664.                         if (ioResult == noErr && SCSI.statusByte != kScsiCommandStatusGood) {
  665.                             ioResult = scsiNonZeroStatus;
  666.                             LogHex(SCSI.statusByte, "\pNon-zero ioResult");
  667.                         }
  668.                     }
  669.                     /*
  670.                      * Release the user I/O request physical memory allocations.
  671.                      */
  672.                     CheckpointIOTable(&REQUEST.scsiIOTable);
  673. LogString("\pAfter Checkpoint user data");
  674.                     /*
  675.                      * Here's where we should checkpoint the PerRequest record. Do not
  676.                      * touch pb after calling IOCommandIsComplete as it may be use
  677.                      * again. This means that we must break out of the while loop.
  678.                      */
  679.                     status = IOCommandIsComplete(REQUEST.ioCommandID, ioResult);
  680. LogStatusString(status, "\pAfter IOCommandIsComplete");
  681.                     break;
  682.                 }
  683.             }
  684.             break;
  685.         }
  686. LogStatusString((OSErr) p2, "\pSecondary Handler Exit");
  687.         return (noErr);
  688. #undef SCSI
  689. #undef PB
  690. }
  691.  
  692. void
  693. StoreDMAParameters(
  694.         PerRequestDataPtr        perRequestDataPtr
  695.     )
  696. {
  697.         UInt32                    ioBufferPhysAddress;
  698.         UInt32                    callerPhysAddress;
  699. #define PB        (*((IOParam *) REQUEST.pb)) 
  700. #define SCSI    (* ((NCRSCSIParamPtr) PB.ioMisc))
  701.  
  702.         Trace(StoreDMAParameters);
  703.         if (REQUEST.dmaLengthPrepared == 0 || SCSI.driverAction == 0) {
  704.             WriteByte(CTEST0, 0);
  705.             SCRIPT.dataTable.address = 0xFFFFFFFFL;
  706.             SCRIPT.dataTable.byteCount = 0;
  707.             //** LogString("\pStoreDMAParameters: nothing prepared");
  708.         }
  709.         else if (REQUEST.scriptSelector == kSCSITestMemoryScript) {
  710.             ioBufferPhysAddress = EndianSwap32Bit((UInt32)
  711.                     REQUEST.scsiIOTable.physicalMapping[REQUEST.physicalMapIndex]
  712.                 );
  713.             callerPhysAddress = EndianSwap32Bit(
  714.                         ((UInt32) SCSI.memTestPhysAddress) + PB.ioActCount
  715.                 );
  716.             REQUEST.memoryMoveScript[0] =
  717.                 EndianSwap32Bit(0xC0000000 | REQUEST.dmaLengthPrepared);
  718.             switch (SCSI.driverAction) {
  719.             case kNCRDriverInputAllowed:    /* PBRead (physAddress->ioBuffer)    */
  720.                 REQUEST.memoryMoveScript[1] = callerPhysAddress;
  721.                 REQUEST.memoryMoveScript[2] = ioBufferPhysAddress;
  722.                 break;
  723.             case kNCRDriverOutputAllowed:    /* PBWrite (ioBuffer->physAddress)    */
  724.                 REQUEST.memoryMoveScript[1] = ioBufferPhysAddress;
  725.                 REQUEST.memoryMoveScript[2] = callerPhysAddress;
  726.                 break;
  727.             }
  728.             PB.ioActCount += REQUEST.dmaLengthPrepared;
  729.             WriteByte(DMODE, SCSI.memTestBurstLength);
  730. #if USE_LOG_LIBRARY
  731.             WriteLogEntry(GLOBAL.logRecordPtr, 'GoIo',
  732.                 LogFormat4(kLogFormatUnsigned, kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  733.                 REQUEST.dmaLengthPrepared,
  734.                 REQUEST.dmaLengthPrepared,
  735.                 SCSI.memTestBurstLength,
  736.                 "\pLen Len Burst"
  737.             );
  738. #endif
  739.         }
  740.         else { /* SCSI command */
  741.             WriteByte(CTEST0, SCSI.driverAction);
  742.             SCRIPT.dataTable.address = EndianSwap32Bit((UInt32)
  743.                         REQUEST.scsiIOTable.physicalMapping[REQUEST.physicalMapIndex]
  744.                     );
  745.             SCRIPT.dataTable.byteCount = EndianSwap32Bit(REQUEST.dmaLengthPrepared);
  746.             /*
  747.              * Increment the actual transfer count -- this is not quite correct,
  748.              * as we haven't actually transferred anything yet. It would be better
  749.              * to increment the count using the actual DMA values, but this would
  750.              * require an extra interrupt or some other fiddling in the script.
  751.              */
  752.             PB.ioActCount += REQUEST.dmaLengthPrepared;
  753. #if 1 && LOG_LIBRARY
  754.             WriteLogEntry(GLOBAL.logRecordPtr, 'GoIO',
  755.                 LogFormat5(kLogFormatUnsigned, kLogFormatUnsigned, kLogFormatAddress,
  756.                     kLogFormatUnsigned, kLogFormatString),
  757.                 (UInt32) REQUEST.dmaLengthPrepared,
  758.                 (UInt32) SCSI.driverAction,
  759.                 EndianSwap32Bit(REQUEST.scriptData.dataTable.address),
  760.                 EndianSwap32Bit(REQUEST.scriptData.dataTable.byteCount),
  761.                 "\pcount act, addr, count"
  762.             );
  763. #endif
  764.         }
  765.         /*
  766.          * We just stored something into the perRequest table that the NCR chip
  767.          * will read. Since we can be called from primary interrupt, we can't
  768.          * call CheckpointIO. SynchronizeIO will do what we need, or so I hope.
  769.          */
  770.         SynchronizeIO();
  771. #undef PB
  772. #undef SCSI
  773. }
  774.  
  775. /*
  776.  * This function starts, or re-starts, the NCR card. The parameter is the script
  777.  * physical address. To restart the script, use the value in the DSP register.
  778.  */
  779. void
  780. StartScript(
  781.         register PerRequestDataPtr perRequestDataPtr,
  782.         UInt32                    scriptPtr
  783.     )
  784. {
  785.         OSErr                    status;
  786.         UInt8                    dmode;                /* 0x38 (for manual restart)    */
  787.         UInt8                    dcntl;                /* 0x3B (for manual restart)    */
  788.  
  789.         //* Trace(StartScript);
  790.         /*
  791.          * Before calling this routine, the driver has messed with the per-request
  792.          * record. Checkpoint it to ensure that the cacheing is coherent.
  793.          */
  794.         status = CheckpointIO(
  795.                     REQUEST.perRequestIOTable.preparationID,
  796.                     kNextIOIsInput | kNextIOIsOutput
  797.                 );
  798.         CheckStatus(status, "\pCheckpoint perRequest restart");
  799.         /*
  800.          * The NCR chip is unhappy if we try to set these bits inside a script.
  801.          *
  802.          * Enable DMA interrupts (register 0x39 0xB9):
  803.          *    bit6        Master Data Parity Error
  804.          *    bit5        Bus Fault
  805.          *    bit4        Aborted
  806.          *    bit3        Single-step interrupt.
  807.          *    bit2        Script interrupt
  808.          *    bit0        Illegal script instruction
  809.          */
  810.         WriteByte(DIEN, bit6 | bit5 | bit4 | bit3 | bit2 | bit0);
  811.         /*
  812.          * SCSI interrupts (register 0x40 0xC0)
  813.          *    bit7        Phase mismatch
  814.          *    bit3        SCSI gross error
  815.          *    bit2        Unexpected disconnect
  816.          *    bit1        Bus reset (from an external device)
  817.          *    bit0        Parity
  818.          * Do not interrupt on function complete, selected, or reselected.
  819.          */
  820.         WriteByte(SIEN0, bit7 | bit3 | bit2 | bit1 | bit0);
  821.         /*
  822.          * SCSI interrupts (register 0x41 0xC1)
  823.          *    bit2        Selection timeout
  824.          *    bit1        General purpose timer (used only for bus reset)
  825.          */
  826.         WriteByte(SIEN1, bit2 | bit1);
  827.         /*
  828.          * Restart the script - it will continue by jumping to one of the loops.
  829.          * -- at least, this is the theory
  830.          */
  831. LogString("\pHere we go");
  832. LogMemory((void *) scriptPtr, 0x20);
  833.         WriteLong(DSP, scriptPtr);
  834.         SynchronizeIO();                            /* eieio                        */
  835.         if (SINGLE_STEP) {
  836.             /*
  837.              * If manual-start is turned on, or we are in "single-step" mode,
  838.              * we must restart the script by setting dcntl bit2.
  839.              */
  840.             dmode = ReadByte(DMODE);                /* Has manual start bit            */
  841.             dcntl = ReadByte(DCNTL);                /* Has single step bit            */
  842.             if ((dmode & bit0) != 0 || (dcntl & bit4) != 0) {
  843.                 WriteByte(DCNTL, dcntl | bit2);
  844.                 SynchronizeIO();                    /* eieio                        */
  845.             }
  846.         }
  847.         LogHex(scriptPtr - ((UInt32) REQUEST.scriptBaseAddress), "\pStartScript");
  848. }
  849.  
  850. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  851.  * StartWatchdogTimeout
  852.  *
  853.  * Start an independent timer that will let us abort a run-away NCR SCSI operation.
  854.  * We ignore any errors.
  855.  *
  856.  * This must be called from Secondary Interrupt Level for now.
  857.  */
  858. void
  859. StartWatchdogTimeout(
  860.         register PerRequestDataPtr perRequestDataPtr
  861.     )
  862. {
  863.         OSStatus                osStatus;
  864.         AbsoluteTime            completionTime;
  865.         NCRSCSIParamPtr            scsiParamPtr;
  866.         volatile AbsoluteTime    now;
  867. #define SCSI    (*scsiParamPtr)
  868.  
  869.         Trace(StartWatchdogTimeout);
  870.         scsiParamPtr = (NCRSCSIParamPtr) (REQUEST.pb)->ioParam.ioMisc;
  871.         if (REQUEST.watchdogTimeout == kNoSCSITimeout
  872.          || REQUEST.watchdogTimeout == durationForever)
  873.              REQUEST.timerID = kInvalidID;
  874.         else {
  875.             now = UpTime();
  876.             completionTime = AddDurationToAbsolute(REQUEST.watchdogTimeout, now);
  877.             osStatus = SetInterruptTimer(
  878.                         &completionTime,
  879.                         WatchdogTimerCompletion,
  880.                         perRequestDataPtr,
  881.                         &REQUEST.timerID
  882.                     );
  883.             CheckStatus(osStatus, "\pSetInterruptTimer");
  884.             if (osStatus != noErr)
  885.                 REQUEST.timerID = kInvalidID;
  886.         }
  887. #undef SCSI
  888. }
  889.  
  890. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  891.  * WatchdogTimerCompletion is a timer completion routine that stops a runaway
  892.  * script.
  893.  */
  894. OSStatus
  895. WatchdogTimerCompletion(
  896.         void                    *p1,    /* PerRequestDataPtr                        */
  897.         void                    *p2        /* Current program counter -- unused        */
  898.     )
  899. {
  900.         UInt8                    istat;
  901.         register PerRequestDataPtr perRequestDataPtr;
  902.  
  903.         Trace(WatchdogTimerCompletion);
  904.         UNUSED(p2);                        /* Current program counter -- unused        */
  905.         perRequestDataPtr = (PerRequestDataPtr) p1;
  906. #if 1 && USE_LOG_LIBRARY
  907.         WriteLogEntry(GLOBAL.logRecordPtr, 'Tout',
  908.             LogFormat5(kLogFormatAddress, kLogFormatAddress,
  909.                 kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  910.             p1, p2, REQUEST.timerID, REQUEST.pb,
  911.             "\pp1, p2, id, pb @ TimerComplete"
  912.         );
  913. #endif
  914.         if (REQUEST.pb != NULL) {
  915.             LogString("\pI/O still running");
  916.             /*
  917.              * The timer fired but I/O is still running. Stop the ckip. Umm, this
  918.              * doesn't really work: we can finish the Macintosh side, but but
  919.              * the SCSI bus will be stuck until we force bus-reset. I don't know
  920.              * the right solution to this problem -- it might require cooperation
  921.              * from the application using this driver. In any case, it is specific
  922.              * to the particular hardware device.
  923.              */
  924.             istat = ReadByte(ISTAT);
  925.             WriteByte(ISTAT, istat | 0x80);            /* Abort NCR Chip                */
  926.             istat = ReadByte(ISTAT);
  927.         }
  928.         return (noErr);
  929. }
  930.  
  931. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  932.  * Cancel a pending timer. This must be called from SecondaryInterrupt level.
  933.  * An atomic sequence is used to prevent the timer from being cancelled twice.
  934.  */
  935. void
  936. CancelWatchdogTimer(
  937.         PerRequestDataPtr        perRequestDataPtr
  938.     )
  939. {
  940.         TimerID                    timerID;
  941.         AbsoluteTime            timeRemaining;
  942.         OSStatus                osStatus;
  943.         
  944.         Trace(CancelWatchdogTimer);
  945.         while ((timerID = REQUEST.timerID) != kInvalidID) {
  946.             if (CompareAndSwap(
  947.                     (UInt32) timerID, kInvalidID, (UInt32 *) &REQUEST.timerID)) {
  948.                 osStatus = CancelTimer(timerID, &timeRemaining);
  949.                 CheckStatus(osStatus, "\pCancelTimer");
  950. #if 0 && LOG_LIBRARY
  951.                 {
  952.                     Nanoseconds                nanosecondsRemaining;
  953.                 
  954.                     nanosecondsRemaining = AbsoluteToNanoseconds(timeRemaining);
  955.                     LogDecimal(nanosecondsRemaining.lo, "\pAfter CancelTimer");
  956.                 }
  957. #endif
  958.                 break;
  959.             }
  960.         }
  961. }
  962.  
  963. #if USE_LOG_LIBRARY
  964. void
  965. LogScriptInterrupt(
  966.         register PerRequestDataPtr perRequestDataPtr
  967.     )
  968. {
  969.         Str255                work;
  970.         const StringPtr        gBusPhase[] = {            /* In sbcl and socl                 */
  971.             "\p DATO", "\p DATI", "\p CMD", "\p STS",
  972.             "\p ResO", "\p ResI", "\p MSGO", "\p MSGI"
  973.         };
  974.  
  975.         //** Trace(LogScriptInterrupt);
  976.         work[0] = 0;
  977.         AppendHexLeadingZeros(work, SHADOW.scriptPCOffset, 3);
  978.         AppendChar(work, ' '); 
  979.         AppendHexLeadingZeros(work, SHADOW.istat, 2);        /* 14    Interrupt status    */
  980.         AppendChar(work, ' '); 
  981.         AppendHexLeadingZeros(work, SHADOW.sbcl, 2);        /* 0B    Active bus status    */
  982.         AppendChar(work, ' '); 
  983.         AppendHexLeadingZeros(work, SHADOW.dstat, 2);        /* 0C    DMA status            */
  984.         AppendChar(work, ' '); 
  985.         AppendHexLeadingZeros(work, SHADOW.sist0, 2);        /* 42    SCSI interrupt stat    */
  986.         AppendChar(work, ' '); 
  987.         AppendHexLeadingZeros(work, SHADOW.sist1, 2);        /* 43    SCSI interrupt stat    */
  988.         if ((SHADOW.sbcl & bit5) == 0)
  989.             PStrCat(work, "\p ~BSY");
  990.         else {
  991.             if ((SHADOW.sbcl & bit7) != 0)
  992.                 PStrCat(work, "\p REQ");
  993.             if ((SHADOW.sbcl & bit6) != 0)
  994.                 PStrCat(work, "\p ACK");
  995.             PStrCat(work, gBusPhase[SHADOW.sbcl & 0x07]);
  996.         }
  997.         WriteLogEntry(GLOBAL.logRecordPtr, ' ISR', LogStringFormat, work);
  998. }
  999.  
  1000. /*
  1001.  * Dump the entire register set.
  1002.  */
  1003. void
  1004. DumpRegisters(
  1005.         register PerRequestDataPtr perRequestDataPtr
  1006.     )
  1007. {
  1008.         int                    i;
  1009.         Str255                work;
  1010.  
  1011.         //** Trace(DumpRegisters);
  1012. #define ncrRegisters    ((UInt32 *) GLOBAL.pciCardBaseAddress)
  1013.         for (i = kRegisterBase; i < kIORegisterMax; i += 16) {
  1014.             work[0] = 0;
  1015.             AppendHexLeadingZeros(work, i, 2);
  1016.             WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1017.                 LogFormat5(kLogFormatAddress, kLogFormatAddress,
  1018.                         kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1019.                 ncrRegisters[(i / sizeof (UInt32)) + 0],
  1020.                 ncrRegisters[(i / sizeof (UInt32)) + 1],
  1021.                 ncrRegisters[(i / sizeof (UInt32)) + 2],
  1022.                 ncrRegisters[(i / sizeof (UInt32)) + 3],
  1023.                 work                
  1024.             );
  1025.         }
  1026.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1027.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1028.             SCRIPT.deviceIDTable.byteCount, SCRIPT.deviceIDTable.address, "\pTarget ID"
  1029.         );
  1030.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1031.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1032.             SCRIPT.idMsgTable.byteCount, SCRIPT.idMsgTable.address, "\pID MSG"
  1033.         );
  1034.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1035.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1036.             SCRIPT.commandTable.byteCount, SCRIPT.commandTable.address, "\pCommand"
  1037.         );
  1038.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1039.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1040.             SCRIPT.dataTable.byteCount, SCRIPT.dataTable.address, "\pData"
  1041.         );
  1042. }
  1043. #endif
  1044.  
  1045.